Blueprint Help Send comments on this topic.
Store Data Objects

Glossary Item Box

Description

CDL provides a number of specific mechanisms for handling data in distributed parallel environments.  Transient store data objects, as the name implies, are destructively 'read', and in many cases provide an equivalent of conventional stack data.  Arbitrated store data objects on the other hand are persistent, and provide an equivalent for file scope and static data. 

In all cases, data objects have two components.  The first of these is the 'data' component itself (see Data Components) which is referenced and managed by the second 'reference' component.  The data component can contain native pointers and references but these will not be valid when data objects are moved between machines, and are unlikely to be valid in asymmetric memory environments.  They can be used however to reference objects within the data component but will need to be 're-linked' each time their owning store is opened for read.

In the case of store data the reference component is referred to as a 'record' and is actually just a pointer and status word with a number of member functions.   Active objects can own 'workspace' objects, and methods can also own 'state' objects.  These are described in more detail below.

In order to implement 'distributable' user data objects there are a number of conventions that need to be adopted and these are described in the sections that follow;

Store Record Components

The record component is a CLIP specific class that derives from a 'ClpRecord' and has a number of standard member functions.  The translator will create a skeletal class definition for each referenced data type, but will assume a fixed size data component.  So if for example a transient store has data type {Dtype} then a file called "DtypeRec.hpp" will be generated and will contain the following class definition;

   class DtypeRec : public DtypeRecBase
   {
   public:
   };

Variable Sized Data

The generated class above does not need modification for fixed size data objects but in the most general case where this is required, the generated code will need to be modified.  Because the data object's size is dynamic, the 'Construct' function needs dimension information.

In order to support dynamically sized data, the record definition would require the following changes;

The 'Size' function needs to be replaced by two overloads, one of which is used by writers, and one which is used by readers.  In the 'writer' case, the function needs to calculate the size based on the object's dimensions, but since the object may not yet be allocated, must be static.  In the Data Components example, the data component had a 'Size' function and so the record simply needs to call that function;

// Writer Case
static Uns DtypeRec::Size( Uns n, Uns m )
{
   return Dtype::Size( n, m );
}

The read case is similar but the dimensions 'N' and 'M' will have been set by the writer (see Construct function below) and so do not need to be re-specified.

// Reader Case
Uns DtypeRec::Size()
{
   return m_Data->Size();
}

The 'Construct' function allocates space for the data object but by convention will also have two overloads that correspond to the two cases where 'keys' are explicit or not explicit.  Note that the explicit case is usually required for automatic connections, whilst the non-explicit case is usually required for manual connections.  The 'Alloc' call below will not necessarily allocate space every time it is called, and its precise behavior will be determined by the owning object's allocation mode attribute.  Typically, objects are only re-allocated if their size changes.

The following code would be required for the variable sized example above;

// Explicit 'key' case will over-ride the previously set 'key'
Uns DtypeRec::Construct( Uns n, Uns m, Int key )
{
   if ( Alloc( Size( n, m ), key ) )
   {
      m_Data->SetDims( n, m );
      return m_Data->Initialise();
   }
   return FALSE;
}

// Alternative case which will preserve previously set 'key'
Uns DtypeRec::Construct( Uns n, Uns m )
{
   if ( Alloc( Size( n, m ) ) )
   {
      m_Data->SetDims( n, m );
      return m_Data->Initialise();
   }
   return FALSE;
}

If the data component convention is followed then these Construct functions are in fact generic and extending to three or more dimensions is then straightforward.  The following 'construct' is generic for store records that require 3 size parameters;

// Explicit 'key' case will over-ride the previously set 'key'
Uns DtypeRec::Construct( Uns n, Uns m, Uns p, Int key )
{
   if ( Alloc( Size( n, m, p ), key ) )
   {
      m_Data->SetDims( n, m, p );
      return m_Data->Initialise();
   }
   return FALSE;
}

// Alternative case which will preserve previously set 'key'
Uns DtypeRec::Construct( Uns n, Uns m, Uns p )
{
   if ( Alloc( Size( n, m, p ) ) )
   {
      m_Data->SetDims( n, m, p );
      return m_Data->Initialise();
   }
   return FALSE;
}